package org.jadapter.tests;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import javax.swing.Icon;

import org.jadapter.Query;
import org.jadapter.func.Filter;
import org.jadapter.func.Function;
import org.jadapter.func.Lambda;
import org.jadapter.tests.examples.CommaSeparated;
import org.jadapter.tests.examples.Incrementer;

@SuppressWarnings("unchecked")
public class TestQuery extends BaseTestCase {           
    
    public void testNEW(){
        Query<String> q = Query.NEW("foo", "bar");
        assertQueryEquals(q, "foo", "bar");  
           
        Query<List<String>> ql = Query.NEW(Arrays.asList("foo", "bar"));
        assertQueryEquals(ql, Arrays.asList("foo", "bar"));  
        assertTrue(ql.iterator().next().iterator().next().equals("foo"));             
    }
    
    
    public void testShouldSliceAndCalculateLength(){
        assertEquals(2, query.slice(1).length());
        assertEquals(1, query.slice(1).slice(1).length());      
        assertEquals(0, query.slice(1).slice(1).slice(1).length());
    }
    
    public void testShouldSliceAndGetElementsAndCalculateLength(){
        assertEquals("a", query.get(0));
        assertEquals(2,   query.slice(1).length());
        assertEquals("b", query.slice(1).get(0));
        assertEquals(1,   query.slice(1).slice(1).length());
        assertEquals("c", query.slice(1).slice(1).get(0));
        assertEquals(0,   query.slice(1).slice(1).slice(1).length());
    }    
      
    
    public void testConstructor(){       
    	assertQueryEquals(query, "a", "b", "c");
    }
    
    public void testSlice(){
    	assertQueryEquals(query.slice(0),   "a", "b", "c");
    	assertQueryEquals(query.slice(0,1), "a", "b");
    	assertQueryEquals(query.slice(0,2), "a", "b", "c");
    	assertQueryEquals(query.slice(1),   "b", "c");  
    	assertQueryEquals(query.slice(1,1), "b");
    	assertQueryEquals(query.slice(1,2), "b", "c");
    	assertQueryEquals(query.slice(2),   "c");
    	assertQueryEquals(query.slice(2,2), "c");
    }
    
    public void testFilter(){
        query = query.filter(new Lambda<Boolean,String>(){
            public Boolean x(String a) {
                return a.equals("b");
            }          
        });
        
        assertQueryEquals(query, "b");
    }
    
    public void testShouldWorkInIteratorLoop(){
        Iterator<String> it = query.iterator();
        int count = 0;
        while(it.hasNext()){
            it.next();
            count++;
        }
        assertEquals(3, count);
    }
    
    public void testShouldWorkUsingIteratorNext(){
        Iterator<String> it = query.iterator();
        it.next();it.next();
        String s = it.next();
        assertEquals("c", s);
    }
    
    public void testSecondIteration() {
        testShouldWorkUsingIteratorNext();
        testShouldWorkUsingIteratorNext();
    }
    
    public void testChaining(){
        query = new Query<String>("a", "au", "e", "ey")
            .map(new Lambda<String,String>(){
                public String x(String t) {
                    return t.toUpperCase(); // capitalize each item
                }         
            })
            .filter(new Filter<String>(){
                public Boolean x(String a) {
                    return a.length() > 1; // remove single letter items
                }            
            })
            .slice(1); // skip the first item
        
        assertQueryEquals(query, "EY"); 
    }
    
    
    public void testLength(){
        assertEquals(3, query.length());
    }
    
    public void testShouldChainSlices(){
    	assertQueryEquals(query.slice(1).slice(1), "c");
    }
    
    public void testShouldAlsoWorkWithStrings() {
        Query<String> query = new Query<String>("fo0"); 
        String foo = "";
        for (String c : query) {
            foo += c;
        }
        
        assertEquals("fo0", foo);
        assertQueryEquals(query.slice(1), "o", "0");
        
        assertEquals(3, query.length());
        assertEquals(2, query.slice(1).length());
        
        assertQueryEquals(query.slice(2),"0");   
        
        assertEquals("o", query.get(1));
        
        
        assertQueryEquals(query.map(new Lambda<String,String>(){
            public String x(String a) {
                return a.toUpperCase();
            }  
        }), "F", "O", "0");
        
        
        Filter<String> numbers = new Filter<String>(){
            public Boolean x(String a) {
                try {
                    Integer.parseInt(a);
                    return true;
                } catch (NumberFormatException e) {
                    return false;
                }          
            }
        };
        
        assertQueryEquals(query.filter(numbers), "0");
        
        Lambda<Integer,String> integers  = new Lambda<Integer,String>(){
            public Integer x(String a) {
                return Integer.parseInt(a);
            }
        };
        
        assertQueryEquals(query.filter(numbers).map(integers), 0);
        
        
    }   
      
    
    @SuppressWarnings("deprecation")
    public void testShouldConcatenateUsingReduce(){
        Function.F2<String,String,String> concat = new Function.F2<String,String,String>(){
            public String invoke(String a0, String a1) {
                return a0 + a1;
            }
            
        };
        
        String s = query.reduce(concat);
        assertEquals("abc", s);

    }    
    
    
    public void testShouldTransformListObjects(){
        registry.register(listAdapter);
        CommaSeparated cs = registry.query(list).as(CommaSeparated.class);
        cs.getCommaSeparated();
    }
    
    public void testShouldProxyIterableObjects(){
        List result = query.as(List.class);
        assertEquals(3, result.size());      
    }    
    
    
    public void testShouldProxyNonIterableObjects(){
        Query<String> q = new Query<String>("foo");
        
        CharSequence result = q.as(CharSequence.class);
        assertEquals(3, result.length()); 
        
        Query<Icon> q1 = new Query<Icon>(new javax.swing.ImageIcon());                
        assertEquals(-1, q1.as(Icon.class).getIconHeight());
              
    }   
    
	class Counter implements Incrementer {
		int count = 0;
		public Counter inc() { count++; return this; }
	}    
    
    public void testShouldInvokeMethodOnAllElements(){
    	    	
    	Counter c0 = new Counter();
    	Counter c1 = new Counter().inc();
    	    	
    	Query<Incrementer> counters = new Query<Incrementer>(c0, c1);
    	counters.each().inc();
    	assertEquals(1, c0.count);
    	assertEquals(2, c1.count);
    	    	
    }
    
    public void testShouldAppendIterables(){
        Query<String> q = query.append(new Query<String>("1", "2"));
        assertQueryEquals(q, "a", "b", "c", "1", "2");
    }
    
    public void testShouldPrependIterables(){
        Query<String> q = query.prepend(new Query<String>("1", "2"));
        assertQueryEquals(q, "1", "2", "a", "b", "c");
    }    
    
             
}
